package com.ruowei.modules.sys.web;

import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ruowei.common.error.exception.BadRequestAlertException;
import com.ruowei.common.querydsl.OptionalBooleanBuilder;
import com.ruowei.common.response.PaginationUtil;
import com.ruowei.modules.sys.domain.enumeration.EmployeeStatusType;
import com.ruowei.modules.sys.domain.enumeration.OfficeStatusType;
import com.ruowei.modules.sys.domain.enumeration.OfficeType;
import com.ruowei.modules.sys.domain.table.QSysOffice;
import com.ruowei.modules.sys.domain.table.SysOffice;
import com.ruowei.modules.sys.pojo.SysOfficeNode;
import com.ruowei.modules.sys.pojo.dto.SysOfficeCityTree;
import com.ruowei.modules.sys.pojo.vm.SysOfficeQM;
import com.ruowei.modules.sys.pojo.vm.SysOfficeTreeSortVM;
import com.ruowei.modules.sys.repository.SysOfficeRepository;
import com.ruowei.modules.sys.repository.entity.SysEmployeeRepository;
import com.ruowei.security.UserModel;
import io.github.jhipster.web.util.ResponseUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import springfox.documentation.annotations.ApiIgnore;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api")
@Api(tags = "组织机构")
public class SysOfficeResource {

    public static final Logger log = LoggerFactory.getLogger(SysOfficeResource.class);
    private static final String ENTITY_NAME = "机构";
    private final SysOfficeRepository sysOfficeRepository;
    private final SysEmployeeRepository sysEmployeeRepository;
    private final QSysOffice qSysOffice = QSysOffice.sysOffice;
    private final JPAQueryFactory queryFactory;

    public SysOfficeResource(SysOfficeRepository sysOfficeRepository, SysEmployeeRepository sysEmployeeRepository, JPAQueryFactory queryFactory) {
        this.sysOfficeRepository = sysOfficeRepository;
        this.sysEmployeeRepository = sysEmployeeRepository;
        this.queryFactory = queryFactory;
    }

    private void recursiveGet(SysOfficeNode sysOfficeNode) {
        sysOfficeNode.setChildren(
            sysOfficeRepository
                .getAllByParentCodeAndStatus(sysOfficeNode.getSysOffice().getOfficeCode(), OfficeStatusType.NORMAL)
                .stream()
                .map(sysOffice -> {
                    SysOfficeNode child = new SysOfficeNode();
                    child.setSysOffice(sysOffice);
                    child.setType(SysOfficeNode.Type.OFFICE);
                    return child;
                }).collect(Collectors.toList())
        );
        sysOfficeNode.getChildren().forEach(this::recursiveGet);
    }

    private void recursiveGetIn(SysOfficeNode sysOfficeNode, List<Long> ids) {
        sysOfficeNode.setChildren(
            sysOfficeRepository.getAllByParentCodeAndIdInOrderByTreeSort(sysOfficeNode.getSysOffice().getOfficeCode(), ids)
                .stream().map(sysOffice -> {
                SysOfficeNode child = new SysOfficeNode();
                child.setSysOffice(sysOffice);
                return child;
            }).collect(Collectors.toList())
        );
        sysOfficeNode.getChildren().forEach(childNode -> recursiveGetIn(childNode, ids));
    }

    @GetMapping("/sys-offices/tree")
    @ApiOperation(value = "获取组织机构树", notes = "作者：李春浩")
    public ResponseEntity<SysOfficeNode> getSysOfficeTree(@ApiIgnore @AuthenticationPrincipal UserModel userModel) {
        // 获取当前机构，如果为一级机构第二层为机构所在城市
        SysOfficeNode sysOfficeNode = sysOfficeRepository
            .getOneByOfficeCodeAndStatus(userModel.getOfficeCode(), OfficeStatusType.NORMAL)
            .map(sysOffice -> {
                SysOfficeNode root = new SysOfficeNode();
                root.setSysOffice(sysOffice);
                root.setType(SysOfficeNode.Type.OFFICE);
                if (StringUtils.isEmpty(sysOffice.getParentCode())) {
                    root.setChildren(
                        queryFactory.selectDistinct(qSysOffice.city)
                            .from(qSysOffice)
                            .where(qSysOffice.id.ne(0L)
                                .and(qSysOffice.deleted.isFalse())
                                .and(qSysOffice.status.eq(OfficeStatusType.NORMAL)))
                            .fetch()
                            .stream()
                            .map(city -> {
                                SysOfficeNode cityRoot = new SysOfficeNode();
                                SysOffice cityOffice = new SysOffice();
                                cityOffice.setOfficeName(city);
                                cityRoot.setSysOffice(cityOffice);
                                cityRoot.setType(SysOfficeNode.Type.CITY);
                                cityRoot.setChildren(
                                    sysOfficeRepository
                                        .getAllByParentCodeAndCityAndStatus(sysOffice.getOfficeCode(), city, OfficeStatusType.NORMAL)
                                        .stream()
                                        .map(thirdSysOffice -> {
                                            SysOfficeNode thirdRoot = new SysOfficeNode();
                                            thirdRoot.setSysOffice(thirdSysOffice);
                                            thirdRoot.setType(SysOfficeNode.Type.OFFICE);
                                            recursiveGet(thirdRoot);
                                            return thirdRoot;
                                        })
                                        .collect(Collectors.toList())
                                );
                                return cityRoot;
                            })
                            .collect(Collectors.toList())
                    );
                } else {
                    recursiveGet(root);
                }
                return root;
            })
            .orElse(new SysOfficeNode());
        return ResponseEntity.ok(sysOfficeNode);
    }

    @PostMapping("/sys-offices")
    @ApiOperation(value = "新增机构", notes = "作者：李春浩")
    public ResponseEntity<Void> createSysOffice(@RequestBody SysOffice sysOffice) {
        sysOfficeRepository.getOneByOfficeName(sysOffice.getOfficeName())
            .ifPresent(so -> {
                throw new BadRequestAlertException("机构名称已存在", ENTITY_NAME, "officeName.exist");
            });
        sysOffice.setOfficeCode(UUID.randomUUID().toString());
        sysOffice.setFullName(sysOffice.getOfficeName());
        sysOffice.setOfficeType(OfficeType.CITY);
        sysOffice.setRemarks(sysOffice.getOfficeName());
        List<String> parentCodes = new ArrayList<>();
        AtomicReference<String> treeNames = new AtomicReference<>(sysOffice.getOfficeName());
        AtomicInteger treeLevel = new AtomicInteger(1);
        if (StringUtils.isNotEmpty(sysOffice.getParentCode())) {
            sysOfficeRepository.getOneByOfficeCode(sysOffice.getParentCode())
                .ifPresent(parentOffice -> {
                    parentCodes.add(parentOffice.getOfficeCode());
                    if (StringUtils.isNotEmpty(parentOffice.getParentCodes())) {
                        parentCodes.addAll(Arrays.asList(parentOffice.getParentCodes().split(",")));
                    }
                    treeNames.set(parentOffice.getTreeNames() + "/" + sysOffice.getOfficeName());
                    treeLevel.set(parentOffice.getTreeLevel() + 1);
                    // 更新treeLeaf
                    sysOfficeRepository.save(parentOffice.treeLeaf(false));
                });
        } else {
            sysOffice.setParentCode(null);
        }

        sysOffice.parentCodes(String.join(",", parentCodes))
            .treeLevel(treeLevel.intValue())
            .treeNames(treeNames.get())
            .status(OfficeStatusType.NORMAL)
            .treeLeaf(true);
        sysOfficeRepository.save(sysOffice);
        return ResponseEntity.ok().build();
    }

    @PostMapping("/sys-offices/edit")
    @Transactional
    @ApiOperation(value = "修改机构", notes = "作者：李春浩")
    public ResponseEntity<Void> updateSysOffice(@RequestBody SysOffice sysOffice) {
        sysOfficeRepository.getOneByOfficeNameAndIdNot(sysOffice.getOfficeName(), sysOffice.getId())
            .ifPresent(sp -> {
                throw new BadRequestAlertException("机构名称已存在", ENTITY_NAME, "officeName.exist");
            });
        sysOfficeRepository.getOneByOfficeCodeAndIdNot(sysOffice.getOfficeCode(), sysOffice.getId())
            .ifPresent(so -> {
                throw new BadRequestAlertException("机构代码已存在", ENTITY_NAME, "officeCode.exist");
            });
        // 判断是否修改了上级机构或机构名称
        SysOffice sysOfficeDB = sysOfficeRepository.getOne(sysOffice.getId());
        if (!Objects.equals(sysOffice.getParentCode(), sysOfficeDB.getParentCode())) {
            sysOfficeRepository.getOneByOfficeCode(sysOffice.getParentCode())
                .map(parentOffice -> {
                    // 判断新的上级机构是否是此机构或此机构的下级机构
                    if (Objects.equals(parentOffice.getOfficeCode(), sysOffice.getOfficeCode())
                        || Arrays.asList(parentOffice.getParentCodes().split(",")).contains(sysOffice.getOfficeCode())) {
                        throw new BadRequestAlertException("不可将上级机构修改为本机构及下属机构", ENTITY_NAME, "parentCode.error");
                    }
                    sysOffice.setParentCodes(parentOffice.getParentCodes() + parentOffice.getOfficeCode() + ",");
                    sysOffice.setTreeNames(parentOffice.getTreeNames() + "/" + sysOffice.getOfficeName());
                    sysOffice.setTreeLevel(parentOffice.getTreeLevel() + 1);
                    sysOffice.setTreeLeaf(false);
                    // 原来的上级机构除了此机构外无其他机构
                    if (StringUtils.isNotEmpty(sysOfficeDB.getParentCode())
                        && sysOfficeRepository.getAllByParentCodeAndOfficeCodeNot(sysOfficeDB.getParentCode(), sysOffice.getOfficeCode()).isEmpty()) {
                        parentOffice.setTreeLeaf(true);
                        sysOfficeRepository.save(parentOffice);
                    }
                    return parentOffice;
                })
                .orElseGet(() -> {
                    sysOffice.setParentCodes("");
                    sysOffice.setTreeNames(sysOffice.getOfficeName());
                    sysOffice.setTreeLevel(1);
                    return sysOffice;
                });
        } else {
            if (!Objects.equals(sysOffice.getOfficeName(), sysOfficeDB.getOfficeName())) {
                sysOfficeRepository.getOneByOfficeCode(sysOffice.getParentCode())
                    .map(parentOffice -> {
                        sysOffice.setTreeNames(parentOffice.getTreeNames() + "/" + sysOffice.getOfficeName());
                        return parentOffice;
                    })
                    .orElseGet(() -> {
                        sysOffice.setTreeNames(sysOffice.getOfficeName());
                        return sysOffice;
                    });
            }
        }
        sysOfficeRepository.save(sysOffice);
        return ResponseEntity.ok().build();
    }

    @PostMapping("/sys-offices/{officeCode}/delete")
    @Transactional
    @ApiOperation(value = "删除机构及下属机构", notes = "作者：李春浩")
    public ResponseEntity<Void> deleteSysOffice(@PathVariable String officeCode) {
        if (!sysEmployeeRepository.findAllByOfficeIdInAndDeletedIsFalseAndStatus(
            sysOfficeRepository.findAllByOfficeCodeOrParentCodeContains(officeCode, officeCode)
                .stream()
                .map(SysOffice::getId)
                .collect(Collectors.toList()),
            EmployeeStatusType.NORMAL).isEmpty()) {
            throw new BadRequestAlertException("此机构下仍有相关用户，无法删除");
        }

        sysOfficeRepository.deleteAllByOfficeCodeOrParentCodesContains(officeCode);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/sys-offices")
    @ApiOperation(value = "机构列表", notes = "作者：李春浩")
    public ResponseEntity<List<SysOfficeNode>> getSysOffices(SysOfficeQM vm, Pageable pageable) {
        // 获取满足查询条件的节点
        List<Long> ids = sysOfficeRepository.getAllNodeByParam(vm.getOfficeId() == null ? Long.valueOf(-1) : vm.getOfficeId(),
            vm.getOfficeCode(), vm.getOfficeName(), vm.getFullName(),
            vm.getOfficeType() == null ? null : vm.getOfficeType().toString(),
            vm.getStatus() == null ? null : vm.getStatus().toString(),
            vm.getCity());
        // 对满足条件的节点按树分组，并取每个树最小层的所有节点
        List<String> treeMinLevels = sysOfficeRepository.getAllTreeMinLevelByParam(ids.size() == 0 ? Collections.singletonList(-1L) : ids)
            .stream().map(treeMinLevel -> treeMinLevel.getTreeName() + treeMinLevel.getTreeLevel())
            .collect(Collectors.toList());
        // 对每个树最小层的节点进行分页
        Page<SysOffice> page = sysOfficeRepository.getAllByTreeMinLevelsAndIds(
            treeMinLevels.size() == 0 ? Collections.singletonList("-1") : treeMinLevels,
            ids.size() == 0 ? Collections.singletonList(-1L) : ids, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "");
        List<SysOfficeNode> sysOfficeNodes = page.getContent().stream().map(rootOffice -> {
            SysOfficeNode parent = new SysOfficeNode();
            parent.setSysOffice(rootOffice);
            recursiveGetIn(parent, ids);
            return parent;
        }).collect(Collectors.toList());
        return new ResponseEntity<>(sysOfficeNodes, headers, HttpStatus.OK);
    }

    @GetMapping("/sys-offices/list")
    @ApiOperation(value = "机构列表（非树结构）")
    public ResponseEntity<List<SysOffice>> getAllSysOffices(SysOfficeQM qm, Pageable pageable) {
        OptionalBooleanBuilder predicate = new OptionalBooleanBuilder()
            .notEmptyAnd(qSysOffice.officeName::contains, qm.getOfficeName())
            .notEmptyAnd(qSysOffice.city::eq, qm.getCity());
        JPAQuery<SysOffice> jpaQuery = queryFactory.selectFrom(qSysOffice)
            .where(predicate.build()
                .and(qSysOffice.id.ne(0L))
                .and(qSysOffice.deleted.isFalse())
                .and(qSysOffice.status.eq(OfficeStatusType.NORMAL)))
            .orderBy(qSysOffice.treeSort.asc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize());
        Page<SysOffice> page = new PageImpl<>(jpaQuery.fetch(), pageable, jpaQuery.fetchCount());
        HttpHeaders headers = io.github.jhipster.web.util.PaginationUtil
            .generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    @GetMapping("/sys-offices/{id}")
    @ApiOperation(value = "机构详情", notes = "作者：李春浩")
    public ResponseEntity<SysOffice> getSysOffice(@PathVariable Long id) {
        Optional<SysOffice> sysOfficeOptional = sysOfficeRepository.findById(id);
        return ResponseUtil.wrapOrNotFound(sysOfficeOptional);
    }

    @GetMapping("sys-offices/cityTree")
    @ApiOperation("机构列表（按城市分组）")
    public ResponseEntity<List<SysOfficeCityTree>> getCityTree() {
        List<SysOfficeCityTree> sysOfficeCityTrees =
            queryFactory.selectDistinct(qSysOffice.city)
                .from(qSysOffice)
                .where(qSysOffice.id.ne(0L)
                    .and(qSysOffice.deleted.isFalse())
                    .and(qSysOffice.status.eq(OfficeStatusType.NORMAL)))
                .fetch()
                .stream()
                .map(city ->
                    new SysOfficeCityTree(city,
                        queryFactory.select(qSysOffice.officeName)
                            .from(qSysOffice)
                            .where(qSysOffice.city.eq(city)
                                .and(qSysOffice.id.ne(0L))
                                .and(qSysOffice.deleted.isFalse())
                                .and(qSysOffice.status.eq(OfficeStatusType.NORMAL)))
                            .orderBy(qSysOffice.treeSort.asc())
                            .fetch()
                    ))
                .collect(Collectors.toList());
        return ResponseEntity.ok(sysOfficeCityTrees);
    }

    @PostMapping("sys-offices/treeSort/edit")
    @ApiOperation("保存排序号")
    @Transactional
    public ResponseEntity<Void> updateTreeSort(@RequestBody List<SysOfficeTreeSortVM> vms) {
        vms.stream().forEach(vm -> sysOfficeRepository.updateTreeSort(vm.getId(), vm.getTreeSort()));
        return ResponseEntity.ok().build();
    }
}
